import { easeInOutQuad } from './utils';
import { onDriverClick } from './events';
import { emit } from './emitter';
import { getConfig } from './config';
import { getState, setState } from './state';
import { PopoverDOM } from './popover';
import { driver } from './driver';
export const allPopovers: PopoverDOM[] = [];

export type StageDefinition = {
  x: number;
  y: number;
  width: number;
  height: number;
};

// This method calculates the animated new position of the
// stage (called for each frame by requestAnimationFrame)
export function transitionStage(elapsed: number, duration: number, from: Element, to: Element) {
  let activeStagePosition = getState('__activeStagePosition');

  const fromDefinition = activeStagePosition ? activeStagePosition : from.getBoundingClientRect();
  const toDefinition = to.getBoundingClientRect();

  const x = easeInOutQuad(elapsed, fromDefinition.x, toDefinition.x - fromDefinition.x, duration);
  const y = easeInOutQuad(elapsed, fromDefinition.y, toDefinition.y - fromDefinition.y, duration);
  const width = easeInOutQuad(
    elapsed,
    fromDefinition.width,
    toDefinition.width - fromDefinition.width,
    duration
  );
  const height = easeInOutQuad(
    elapsed,
    fromDefinition.height,
    toDefinition.height - fromDefinition.height,
    duration
  );

  activeStagePosition = {
    x,
    y,
    width,
    height
  };

  renderOverlay(activeStagePosition);
  setState('__activeStagePosition', activeStagePosition);
}

export function trackActiveElement(element: Element) {
  if (!element) {
    return;
  }

  const definition = element.getBoundingClientRect();

  const activeStagePosition: StageDefinition = {
    x: definition.x,
    y: definition.y,
    width: definition.width,
    height: definition.height
  };

  setState('__activeStagePosition', activeStagePosition);

  renderOverlay(activeStagePosition);
}

export function refreshOverlay() {
  const activeStagePosition = getState('__activeStagePosition');
  const overlaySvg = getState('__overlaySvg');

  if (!activeStagePosition) {
    return;
  }

  if (!overlaySvg) {
    console.warn('No stage svg found.');
    return;
  }

  const windowX = window.innerWidth;
  const windowY = window.innerHeight;

  overlaySvg.setAttribute('viewBox', `0 0 ${windowX} ${windowY}`);
}

function mountOverlay(stagePosition: StageDefinition) {
  const overlaySvg = createOverlaySvg(stagePosition);
  document.body.appendChild(overlaySvg);

  onDriverClick(overlaySvg, (e) => {
    const target = e.target as SVGElement;
    if (target.tagName !== 'path') {
      return;
    }

    emit('overlayClick');
  });

  setState('__overlaySvg', overlaySvg);

  const allowSkipButton = getConfig('overlaySkipButton');

  if (allowSkipButton && allowSkipButton !== '') {
    const skipButton = document.createElement('div');
    skipButton.id = 'driver-popover-overlay-skip-btn';
    skipButton.classList.add('driver-popover-overlay-skip-btn');
    skipButton.innerText = getConfig('overlaySkipButton') || '';

    document.body.appendChild(skipButton);

    onDriverClick(skipButton, () => {
      emit('skipButtonClick');
    });

    setState('__overlaySkipBtn', skipButton);
  }
}

function renderOverlay(stagePosition: StageDefinition) {
  const overlaySvg = getState('__overlaySvg');

  // TODO: cancel rendering if element is not visible
  if (!overlaySvg) {
    mountOverlay(stagePosition);

    return;
  }

  const pathElement = overlaySvg.firstElementChild as SVGPathElement | null;
  if (pathElement?.tagName !== 'path') {
    throw new Error('no path element found in stage svg');
  }

  pathElement.setAttribute('d', generateStageSvgPathString(stagePosition));
}

function createOverlaySvg(stage: StageDefinition): SVGSVGElement {
  const windowX = window.innerWidth;
  const windowY = window.innerHeight;

  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  svg.classList.add('driver-overlay', 'driver-overlay-animated');

  svg.setAttribute('viewBox', `0 0 ${windowX} ${windowY}`);
  svg.setAttribute('xmlSpace', 'preserve');
  svg.setAttribute('xmlnsXlink', 'http://www.w3.org/1999/xlink');
  svg.setAttribute('version', '1.1');
  svg.setAttribute('preserveAspectRatio', 'xMinYMin slice');

  svg.style.fillRule = 'evenodd';
  svg.style.clipRule = 'evenodd';
  svg.style.strokeLinejoin = 'round';
  svg.style.strokeMiterlimit = '2';
  svg.style.zIndex = '10000';
  svg.style.position = 'fixed';
  svg.style.top = '0';
  svg.style.left = '0';
  svg.style.width = '100%';
  svg.style.height = '100%';

  const stagePath = document.createElementNS('http://www.w3.org/2000/svg', 'path');

  stagePath.setAttribute('d', generateStageSvgPathString(stage));

  stagePath.style.fill = getConfig('overlayColor') || 'rgb(0,0,0)';
  stagePath.style.opacity = `${getConfig('overlayOpacity')}`;
  stagePath.style.pointerEvents = 'auto';
  stagePath.style.cursor = 'auto';

  svg.appendChild(stagePath);

  return svg;
}

function generateStageSvgPathString(stage: StageDefinition) {
  const windowX = window.innerWidth;
  const windowY = window.innerHeight;

  const stagePadding = getConfig('stagePadding') || 0;
  const stageRadius = getConfig('stageRadius') || 0;

  const stageWidth = stage.width + stagePadding * 2;
  const stageHeight = stage.height + stagePadding * 2;

  // prevent glitches when stage is too small for radius
  const limitedRadius = Math.min(stageRadius, stageWidth / 2, stageHeight / 2);

  // no value below 0 allowed + round down
  const normalizedRadius = Math.floor(Math.max(limitedRadius, 0));

  const highlightBoxX = stage.x - stagePadding + normalizedRadius;
  const highlightBoxY = stage.y - stagePadding;
  const highlightBoxWidth = stageWidth - normalizedRadius * 2;
  const highlightBoxHeight = stageHeight - normalizedRadius * 2;

  return `M${windowX},0L0,0L0,${windowY}L${windowX},${windowY}L${windowX},0Z
    M${highlightBoxX},${highlightBoxY} h${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},${normalizedRadius} v${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},${normalizedRadius} h-${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},-${normalizedRadius} v-${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},-${normalizedRadius} z`;
}

export function destroyOverlay() {
  const overlaySvg = getState('__overlaySvg');
  if (overlaySvg) {
    overlaySvg.remove();
  }
}

export function destroyOverlayBtn() {
  const overlayBtn = getState('__overlaySkipBtn');
  if (overlayBtn) {
    overlayBtn.remove();
  }
}
